<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            margin:0;
            padding: 0;
        }
        #grid{
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            grid-gap: 10px;
            padding: 10px;
        }
        .item{
            height: 200px;
            border-radius: 10px;
            background-color: palevioletred;
            display: flex;
            justify-content: center;
            align-items: center;
            user-select: none;
            transform-origin: 0 0;
        }
        /* 第一种居中方法: 应用两个文本方式进行居中 */
        /* #dialog-wrapper{
            display: none;
            position: absolute;
            left: 0;
            right: 0;
            top: 0;
            bottom: 0;
            z-index: 9;
            text-align: center;
        }
        #dialog-wrapper::after{
            content: '';
            display: inline-block;
            vertical-align: middle;
            height: 100%;
        }
        #dialog{
            text-align: initial;
            display: inline-block;
            vertical-align: middle;
            --h: 680px;
            --w: 80%;
            width: var(--w);
            height: var(--h);
            background-color: palegreen;
            border-radius: 10px;
        } */
        #dialog-wrapper{
            display: none;
            position: fixed;
            left: 0;
            right: 0;
            top: 0;
            bottom: 0;
            z-index: 9;
            height: 100%;
            overflow-x: hidden;
        }
        /* 负margin居中 */
        #dialog{
            --h: 680px;
            --w: 80%;
            /* position: absolute;
            left: 50%;
            top: 50%;
            margin-top: calc(-1 * var(--h) / 2);
            margin-left: calc(-1 * var(--w) / 2); */
            width: var(--w);
            height: var(--h);
            margin: 5vh auto;
            background-color: palegreen;
            border-radius: 10px;
        }
    </style>
</head>
<body>
    <div id="grid">
        <div class="item">2</div>
        <div class="item">3</div>
        <div class="item">4</div>
        <div class="item">5</div>
        <div class="item">6</div>
        <div class="item">7</div>
        <div class="item">8</div>
        <div class="item">9</div>
    </div>
    <div id="dialog-wrapper">
        <div id="dialog">这是弹窗内容</div>
    </div>
    <script>
        const dialogWrapper = document.querySelector('#dialog-wrapper');
        const dialog = dialogWrapper.querySelector('#dialog');
        let curItem = null;
        const options = {
            duration: 300,
            easing: 'linear',
            fill: 'both'
        };
        // flip： 反序动画
        document.querySelector('#grid').addEventListener('click', (e) => {
            document.documentElement.style.setProperty('overflow', 'hidden');
            curItem = e.target;
            if(!curItem.classList.contains("item")) {return}
            // first 起始状态
            const { left: firstLeft, top: firstTop, width: firstW, height: firstH } = curItem.getBoundingClientRect();
            
            // do something width and height cause page layout（重排） 
            dialogWrapper.style.setProperty('display', 'block');
            dialog.style.setProperty('transform-origin', '0 0');
            
            // last 最终状态
            const { left: lastLeft, top: lastTop, width: lastW, height: lastH } = dialog.getBoundingClientRect();
            
            // invert： 从“最后”位置出发，将元素反转回“第一个”位置，并使用该转换作为起点
            const deltaX = firstLeft - lastLeft;
            const deltaY = firstTop - lastTop;
            const scaleX = firstW / lastW;
            const scaleY = firstH / lastH;

            dialogWrapper.animate([
                { backgroundColor: 'rgba(0,0,0,0)' },
                { backgroundColor: 'rgba(0,0,0,.6)' }
            ], options)

            // 一定要先tanslate定位，在scale缩放
            // flip动画，是对last状态的元素进行动画
            const ani = dialog.animate([            
                { transform: `translate(${deltaX}px,${deltaY}px) scale(${scaleX}, ${scaleY})`, opacity: 0 },
                { transform: 'translate(0, 0) scale(1)', opacity: 1 }
            ], options);
        });

        //sequence: 正序动画
        dialogWrapper.addEventListener('click', (e) => {
            const target = e.target;
            if(!target.contains(dialogWrapper)){return}

            // first 起始状态
            const { left: firstLeft, top: firstTop, width: firstW, height: firstH } = dialog.getBoundingClientRect();

            // do something width and height cause page layout（重排）
            dialog.style.setProperty('transform-origin', '0 0');
            dialogWrapper.style.setProperty('display', 'block');
            
            // last 最终状态
            const { left: lastLeft, top: lastTop, width: lastW, height: lastH } = curItem.getBoundingClientRect();
            
            // 正序动画，与flip动画的值，互为反数
            const deltaX = -1 * (firstLeft - lastLeft);
            const deltaY = -1 * (firstTop - lastTop);
            const scaleX = 1 / (firstW / lastW);
            const scaleY = 1 / (firstH / lastH);

            dialogWrapper.animate([
                { backgroundColor: 'rgba(0,0,0,.6)' },
                { backgroundColor: 'rgba(0,0,0,0)' }
            ], options);

            // 一定要先tanslate定位，在scale缩放
            // sequence动画，是对first状态的元素进行动画
            const ani = dialog.animate([
                { transform: 'translate(0,0) scale(1)', opacity: 1 },            
                { transform: `translate(${deltaX}px,${deltaY}px) scale(${scaleX}, ${scaleY})`, opacity: 0 },     
            ], Object.assign({}, options, { fill: 'none' }));
            ani.finished.then(() => {
                dialogWrapper.style.setProperty('display', 'none');
                document.documentElement.removeAttribute('style')
            })
        });
        /**
         * 总结
         * sequence动画,最后一帧，恢复至初始状态，即fill:none
         */
    </script>
</body>
</html>